////////////////////////////////////////////////////////////////////////////////
/// Midnight Circus 2007
////////////////////////////////////////////////////////////////////////////////

namespace GatherTogether
{
    /// <summary>
    /// The base class of exceptions that will be thrown by the dumper when
    /// it encounters errors.
    /// </summary>
    class DumperException : System.ApplicationException
    {
        public DumperException(string message)
            : base(message)
        {
        }
    }

    class Dumper
    {
        private static System.IO.StreamWriter streamWriter = null;
        private static int sIndent = 0;

        public static void Dump(string filename, GatherDB gatherDB)
        {
            streamWriter = new System.IO.StreamWriter(filename);
            // TODO handle failure

            DumpGatherItems(gatherDB.Items);
            DumpGatherDrops(gatherDB.Drops);
            DumpGatherDropRates(gatherDB.DropRates);

            // TODO throw exception if sIndent is not 0

            // write out the miscellaneous setting information
            // HACK: this is currently working because of our wonderful hacks in the parse (see Parser.cs)
            streamWriter.WriteLine(gatherDB.Settings);

            streamWriter.Close();
        }

        private static void StartBlock(string line)
        {
            DumpLine(line);
            ++sIndent;
        }

        private static void EndBlock(string line)
        {
            --sIndent;
            DumpLine(line);
        }

        private static void DumpLine(string line)
        {
            // start the line with tabs
            streamWriter.Write(new string('\t', sIndent));

            // finish the line with the specified string
            streamWriter.WriteLine(line);
        }

        private static void DumpGatherItems(GatherItems.GatherItemsDB gatherItemsDB)
        {
            StartBlock("GatherItems = {");

            for (int continentID = 0; continentID < GatherConstants.NumberOfContinents; ++ continentID)
            {
                StartBlock(string.Format("[{0}] = {{", continentID + 1)); // +1 due to 1-based index in lua file

                DumpContinent(gatherItemsDB[continentID]);

                EndBlock(string.Format("}}, -- [{0}]", continentID + 1)); // +1 due to 1-based index in lua file
            }

            DumpLine("[\"dbVersion\"] = 2,");

            EndBlock("}"); // GatherItems
        }

        private static void DumpContinent(GatherItems.Continent continent)
        {
            // the id for the continent was dumped in DumpGatherItems (because a continent doesnt know its own id)
            foreach (GatherItems.Area area in continent)
            {
                DumpArea(area);
            }
        }

        private static void DumpArea(GatherItems.Area area)
        {
            StartBlock(string.Format("[\"{0}\"] = {{", GatherConstants.AreaName.GetAreaName(area.Zone)));

            foreach (GatherItems.Node node in area)
            {
                DumpNode(node);
            }

            EndBlock("},");
        }

        private static void DumpNode(GatherItems.Node node)
        {
            // We need to handle a special case where power crystals are not represented by an integer ID number
            // but by the special string "TREASURE_POWERCRYST" 
            if (node.type != GatherItems.Node.GatherType.BluePowerCrystal)
                StartBlock(string.Format("[{0}] = {{", (ulong)node.type));
            else
                StartBlock("[\"TREASURE_POWERCRYST\"] = {");

            int locationCount = 1;
            foreach (GatherItems.Location location in node)
            {
                StartBlock("{");

                DumpLocation(location);

                EndBlock(string.Format("}}, -- [{0}]", locationCount++));
            }

            DumpLine(string.Format("[\"gtype\"] = \"{0}\",", node.typeString));

            EndBlock("},");
        }

        private static void DumpLocation(GatherItems.Location location)
        {
            DumpLine(string.Format("{0}, -- [1]", location.x));
            DumpLine(string.Format("{0}, -- [2]", location.y));
            DumpLine(string.Format("{0}, -- [3]", location.count));
            DumpLine(string.Format("{0}, -- [4]", location.harvested));
            DumpLine(string.Format("{0}, -- [5]", location.inspected));

            // only need to dump the source if there is one
            if (location.source != string.Empty)
            {
                DumpLine(string.Format("{0}, -- [6]", location.source));
            }
        }

        private static void DumpGatherDrops(GatherDrops.GatherDropsDB gatherDropsDB)
        {
            // GatherDrops = {
            //     [1731] = {
            //         [2770] = 1,
            //         ["total"] = 1,
            //     },
            // }

            if (gatherDropsDB.Count == 0)
            {
                DumpLine("GatherDrops = nil");
            }
            else
            {
                StartBlock("GatherDrops = {");

                foreach (GatherDrops.Node node in gatherDropsDB)
                {
                    StartBlock(string.Format(@"[{0}] = {{", node.Id));

                    foreach (GatherDrops.Loot loot in node)
                    {
                        DumpLine(string.Format(@"[{0}] = {1},", loot.Id, loot.Count));
                    }

                    EndBlock("},"); // node
                }

                EndBlock("}"); // GatherDrops
            }
        }

        private static void DumpGatherDropRates(GatherDropRates.GatherDropRatesDB gatherDropRatesDB)
        {
            StartBlock("Gatherer_DropRates = {");

            for (int continentID = 0; continentID < GatherConstants.NumberOfContinents; ++continentID)
            {
                StartBlock("{"); // +1 due to 1-based index in lua file

                DumpContinentDropRates(gatherDropRatesDB[continentID]);

                EndBlock(string.Format("}}, -- [{0}]", continentID + 1)); // +1 due to 1-based index in lua file
            }

            DumpLine("[\"dbVersion\"] = 1,");

            EndBlock("}"); // GatherDropRates
        }

        private static void DumpContinentDropRates(GatherDropRates.Continent continent)
        {
            // the id for the continent was dumped in DumpGatherDropRates (because a continent doesnt know its own id)
            foreach (GatherDropRates.Area area in continent)
            {
                DumpAreaDropRates(area);
            }
        }

        private static void DumpAreaDropRates(GatherDropRates.Area area)
        {
            StartBlock(string.Format("[\"{0}\"] = {{", GatherConstants.AreaName.GetAreaName(area.Zone)));

            foreach (GatherDropRates.Node node in area)
            {
                DumpNodeDropRates(node);
            }

            EndBlock("},");
        }

        private static void DumpNodeDropRates(GatherDropRates.Node node)
        {
            StartBlock(string.Format(@"[{0}] = {{", node.Id));

            foreach (GatherDropRates.Loot loot in node)
            {
                DumpLine(string.Format(@"[{0}] = {1},", loot.Id, loot.Count));
            }

            EndBlock("},"); // node
        }
    }
}